﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Scan
{
    public class TreeViewEx : TreeView
    {
        /// <summary>
        /// The original parent of this control.
        /// </summary>
        Control prevParent = null;

        /// <summary>
        /// Creates a new instance of the derived TreeView control
        /// </summary>
        public TreeViewEx()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Called when the control's parent is changed. Here we hook into that
        /// parent's WndProc and spy on the WM_NOTIFY message. When the parent
        /// changes, we unhook the old parent's WndProc and hook into the new one.
        /// </summary>
        /// <param name="e">The arguments for this event</param>
        protected override void OnParentChanged(EventArgs e)
        {
            if (this.Site == null || !this.Site.DesignMode)
            {
                // unhook the old parent
                if (this.prevParent != null)
                {
                    WndProcHooker.UnhookWndProc(prevParent, Win32.WM_NOTIFY);
                }
                // update the previous parent
                prevParent = this.Parent;

                // hook up the new parent
                if (this.Parent != null)
                {
                    WndProcHooker.HookWndProc(this.Parent,
                        new WndProcHooker.WndProcCallback(this.WM_Notify_Handler),
                        Win32.WM_NOTIFY);
                }
            }

            base.OnParentChanged(e);
        }

        /// <summary>
        /// Occurs when the user clicks a TreeNode with the mouse.
        /// </summary>
        public event TreeNodeMouseClickEventHandler NodeMouseClick;

        /// <summary>
        /// Occurs when the mouse pointer is over the control and a mouse button is clicked. 
        /// </summary>
        /// <param name="e">Provides data for the NodeMouseClick event.</param>
        protected void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
        {
            if (NodeMouseClick != null)
                NodeMouseClick(this, e);
        }

        /// <summary>
        /// The method that gets called when a WM_NOTIFY message is received by the
        /// TreeView's parent.
        /// </summary>
        /// <param name="hwnd">The handle of the window that received the message</param>
        /// <param name="msg">The message received</param>
        /// <param name="wParam">The wParam arguments for the message</param>
        /// <param name="lParam">The lParam arguments for the message</param>
        /// <param name="handled">Set to true to indicate that this message was handled</param>
        /// <returns>An appropriate returen code for the message handled (see MSDN)</returns>
        int WM_Notify_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lParam,
            ref bool handled)
        {
            Win32.NMHDR nmHdr = new Win32.NMHDR();
            System.Runtime.InteropServices.Marshal.PtrToStructure((IntPtr)lParam, nmHdr);
            switch (nmHdr.code)
            {
                case Win32.NM_RCLICK:
                case Win32.NM_CLICK:
                    // get the cursor coordinates on the client
                    Point msgPos = Win32.LParamToPoint((int)Win32.GetMessagePos());
                    msgPos = this.PointToClient(msgPos);

                    // check to see if the click was on an item
                    Win32.TVHITTESTINFO hti = new Win32.TVHITTESTINFO();
                    hti.pt.X = msgPos.X;
                    hti.pt.Y = msgPos.Y;
                    int hitem = Win32.SendMessage(this.Handle, Win32.TVM_HITTEST, 0, ref hti);
                    uint htMask = (
                        Win32.TVHT_ONITEMICON |
                        Win32.TVHT_ONITEMLABEL |
                        Win32.TVHT_ONITEMINDENT |
                        Win32.TVHT_ONITEMBUTTON |
                        Win32.TVHT_ONITEMRIGHT |
                        Win32.TVHT_ONITEMSTATEICON);
                    if ((hti.flags & htMask) != 0)
                    {
                        bool leftButton = (nmHdr.code == Win32.NM_CLICK);
                        RaiseNodeMouseClickEvent(hti.hItem,
                            leftButton ? MouseButtons.Left : MouseButtons.Right,
                            msgPos);
                        return 0;
                    }
                    break;

                default:
                    break;
            }
            return 0;
        }

        /// <summary>
        /// Raises the TreeNodeMouseClick event for the TreeNode with the specified handle.
        /// </summary>
        /// <param name="hNode">The handle of the node for which the event is raised</param>
        /// <param name="button">The [mouse] buttons that were pressed to raise the event</param>
        /// <param name="coords">The [client] cursor coordinates at the time of the event</param>
        void RaiseNodeMouseClickEvent(IntPtr hNode, MouseButtons button, Point coords)
        {
            TreeNode tn = FindTreeNodeFromHandle(this.Nodes, hNode);

            TreeNodeMouseClickEventArgs e = new TreeNodeMouseClickEventArgs(
                tn,
                button,
                1, coords.X, coords.Y);

            OnNodeMouseClick(e);
        }

        /// <summary>
        /// Finds a TreeNode in the provided TreeNodeCollection that has the handle specified.
        /// Warning: recursion!
        /// </summary>
        /// <param name="tnc">The TreeNodeCollection to search</param>
        /// <param name="handle">The handle of the TreeNode to find in the collection</param>
        /// <returns>The TreeNode if found; null otherwise</returns>
        TreeNode FindTreeNodeFromHandle(TreeNodeCollection tnc, IntPtr handle)
        {
            foreach (TreeNode tn in tnc)
            {
                if (tn.Handle == handle) return tn;
                // we couldn't have clicked on a child of this node if this node
                // is not expanded!
                if (tn.IsExpanded)
                {
                    TreeNode tn2 = FindTreeNodeFromHandle(tn.Nodes, handle);
                    if (tn2 != null) return tn2;
                }
            }
            return null;
        }

        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">如果应释放托管资源，为 true；否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary>
        /// 设计器支持所需的方法 - 不要
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }

        #endregion
    }
}


namespace System.Windows.Forms
{
    /// <summary>
    /// Represents the method that will handle the NodeMouseClick event of a TreeView
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A TreeNodeMouseClickEventArgs that contains the event data.</param>
    public delegate void TreeNodeMouseClickEventHandler(object sender, TreeNodeMouseClickEventArgs e);

    /// <summary>
    /// Provides data for the System.Windows.Forms.TreeView.NodeMouseClick event
    /// </summary>
    public class TreeNodeMouseClickEventArgs : MouseEventArgs
    {
        /// <summary>
        /// Initializes a new instance of the TreeNodeMouseClickEventArgs class.
        /// </summary>
        /// <param name="node">The node that was clicked</param>
        /// <param name="button">One of the System.Windows.Forms.MouseButtons members</param>
        /// <param name="clicks">The number of clicks that occurred</param>
        /// <param name="x">The x-coordinate where the click occurred</param>
        /// <param name="y">The y-coordinate where the click occurred</param>
        public TreeNodeMouseClickEventArgs(TreeNode node, MouseButtons button, int clicks, int x, int y) :
            base(button, clicks, x, y, 0)
        {
            nodeValue = node;
        }

        /// <summary>
        /// Gets the node that was clicked.
        /// </summary>
        public TreeNode Node
        {
            get { return nodeValue; }
            set { nodeValue = value; }
        }
        TreeNode nodeValue;

        public override string ToString()
        {
            return string.Format(
                "TreeNodeMouseClickEventArgs\r\n\tNode: {0}\r\n\tButton: {1}\r\n\tX: {2}\r\n\tY: {3}",
                nodeValue.Text, Button.ToString(), X, Y);
        }
    }
}
